#include "sketchcontroller.h"
#include "Tetrahedron.h"
#include <QtOpenGL>

Curve3D toCurve(const QVector<QVector3D>& v)
{
    Curve3D c;
    for(int i = 0; i < v.size(); ++i)
    {
        PointN<float,3> p;
        p[0] = v[i].x();
        p[1] = v[i].y();
        p[2] = v[i].z();
        c.add(p);
    }
    return c;
}

QVector3D toQVector3D(const VectorN<float,3>& v)
{
    return QVector3D(v[0],v[1],v[2]);
}

SketchController::SketchController(Tetrahedron *t, QObject *parent) :
    QObject(parent)
{
    tetra = t;
    showLines = true;
    showPoints = true;

    connect(tetra, SIGNAL(afterDraw()),
            this, SLOT(draw()));
}


void SketchController::mouseRigthMove(QPoint begin, QPoint curr)
{
    temp.append(QVector2D(curr));
}

void SketchController::clear()
{
    curves.clear();
    points.clear();
    normmals.clear();
}

void SketchController::mouseRigthFinish(QPoint ini, QPoint curr)
{
    PolygonalCurve<float, 2> tCurve;
    tCurve.clear();

    if(curves.size() > 1)
        clear();

    for(int i = 0; i < temp.size(); ++i)
    {
        PointN<float, 2> p;
        p[0] = temp[i].x();
        p[1] = temp[i].y();
        tCurve.add(p);
    }

    PolygonalCurve<float, 2> tCurve2;
    tCurve.douglasPeuckerDecimate(tCurve2,4);

    curve2D.clear();
    for(int i = 0; i < tCurve2.size(); ++i)
    {
        curve2D.append(QVector2D(tCurve2[i][0], tCurve2[i][1]));
    }

    temp.clear();
    processaCurva();
}

QVector3D bariCoord(const TRIANGLE& t, const QVector2D& p)
{
   float areaTotal;
   QVector3D area;
   QVector3D p0 = QVector3D(t.v[0].toVector2DAffine(), 0.0);
   QVector3D p1 = QVector3D(t.v[1].toVector2DAffine(), 0.0);
   QVector3D p2 = QVector3D(t.v[2].toVector2DAffine(), 0.0);
   QVector3D pp = QVector3D(p, 0.0);

   areaTotal = (QVector3D::crossProduct(p1-p0, p2-p0)).length();

   area.setX((QVector3D::crossProduct(p2-p1,pp-p1)).length()/areaTotal);
   area.setY((QVector3D::crossProduct(p0-p2,pp-p2)).length()/areaTotal);
   area.setZ((QVector3D::crossProduct(p1-p0,pp-p0)).length()/areaTotal);

   return area;
}

TRIANGLE SketchController::toDeviceSpace(const TRIANGLE& t)
{
    TRIANGLE ret;
    QMatrix4x4 mVM = tetra->modelViewMatrix();
    QMatrix4x4 pM = tetra->projectionMatrix();

    QMatrix4x4 mvPM = pM * mVM;

    for(int j = 0; j < 3; ++j) ret.v[j] = (mvPM * t.v[j]);

    return ret;
}

QVector3D SketchController::toObjectSpace(const QVector3D& v)
{
    QMatrix4x4 mVM = tetra->modelViewMatrix();
    QMatrix4x4 pM = tetra->projectionMatrix();

    QMatrix4x4 mvPM = pM * mVM;
    mvPM = mvPM.inverted();

    QVector4D ret = mvPM * QVector4D(v,1.0);
    return ret.toVector3DAffine();
}

void SketchController::processaCurva()
{
    TRIANGLE frente = tetra->getTrianguloFrente();
    frente = toDeviceSpace(frente);

    int vwp[4];
    glGetIntegerv(GL_VIEWPORT, vwp);

    for(int i = 0; i < curve2D.size(); ++i)
    {

        curve2D[i].setX(2.0*(curve2D[i].x() - vwp[0]));
        curve2D[i].setY(2.0*(curve2D[i].y() - vwp[1]));

        curve2D[i].setX(curve2D[i].x()/vwp[2] -1.0);
        curve2D[i].setY(-(curve2D[i].y()/vwp[3] -1.0));

        QVector3D nP = bariCoord(frente,curve2D[i]);

        QVector3D zs(frente.v[0].z(),frente.v[1].z(), frente.v[2].z());
        QVector3D dS(curve2D[i],QVector3D::dotProduct(nP,zs));
        curve3D.append( toObjectSpace(dS));
    }

    curves.append(toCurve(curve3D));
    curve3D.clear();
    if(curves.size() == 2)
        makeSurface();
}

void SketchController::draw()
{
    glDisable(GL_LIGHTING);
    glColor3f(1.0,1.0,1.0);
    if(showLines)
    {
        for(int i = 0; i < curves.size(); ++i)
        {
            glBegin(GL_LINE_STRIP);
            {
                for(int j = 0; j < curves[i].size(); j++)
                {
                    glVertex3f(curves[i][j][0], curves[i][j][1], curves[i][j][2]);
                }
            }glEnd();
        }
    }

    if(showPoints)
    {
        glBegin(GL_POINTS);
        {
            for(int i = 0; i < points.size(); i++)
            {
                glVertex3f(points[i].x(), points[i].y(), points[i].z());
            }
        }glEnd();
    }
    glColor3f(0.0,0.0,1.0);
    glEnable(GL_LIGHTING);
}

void SketchController::connectPoints()
{
    Curve3D c1 = curves[0];
    Curve3D c2 = curves[1];

    VectorN<float,3> v = c1[0] - c2[0];
    QPair<int,int> idx = qMakePair(0,0);
    double dist = v.norm();

    VectorN<float,3> tv = c1[0] - c2[c2.size()-1];
    double tDist = tv.norm();
    if(tDist < dist)
    {
        dist = tDist;
        v = tv;
        idx = qMakePair(0, (int)(c2.size()-1));
    }

    tv = c1[c1.size()-1] - c2[0];
    tDist = tv.norm();
    if(tDist < dist)
    {
        dist = tDist;
        v = tv;
        idx = qMakePair((int)c1.size()-1, 0);
    }

    tv = c1[c1.size()-1] - c2[c2.size()-1];
    tDist = tv.norm();
    if(tDist < dist)
    {
        dist = tDist;
        v = tv;
        idx = qMakePair((int)c1.size()-1, (int)c2.size()-1);
    }

    for(int i=  0; i < c2.size(); ++i)
    {
        c2[i] = v + c2[i];
    }

    if(idx.first > 0)
        c1.reverse();
    if(idx.second > 0)
        c2.reverse();

    curves[0] = c1;
    curves[1] = c2;
}

void SketchController::samplerPoints()
{
    points.clear();
    normmals.clear();

    Curve3D c1;
    Curve3D c2;

    BernsteinPolyLine b1;
    BernsteinPolyLine b2;
    b1.bindControlPoints(curves[0]);
    b2.bindControlPoints(curves[1]);
    c1 = b1.getPolyLine();
    c2 = b2.getPolyLine();

    for(int i = 0; i < c1.size(); ++i)
    {
        VectorN<float,3> v = c1[i] - c2[0];

        QVector3D dc1 = toQVector3D(c1.tangent(i));

        for(int j = 0; j < c2.size(); ++j)
        {
            PointN<float,3> p = v + c2[j];

            if(!tetra->dentro(QVector4D(p[0], p[1], p[2], 1.0)))
                continue;

            QVector3D dc2 = toQVector3D(c2.tangent(j));

            QVector3D n = QVector3D::crossProduct(dc1,dc2);
            n.normalize();

            points.append(QVector3D(p[0], p[1], p[2]));
            normmals.append(n);
        }
    }
}

void SketchController::makeSurface()
{
    connectPoints();
    samplerPoints();
    emit samplerPointsDone();
}

const QVector<QVector3D>& SketchController::getPoints()
{
    return points;
}

QVector<QPair<QVector3D, QVector3D> > SketchController::getPointsAndNormals()
{
    QVector<QPair<QVector3D, QVector3D> > ret;

    for(int i = 0; i < points.size(); ++i)
    {
        ret.append(qMakePair(points[i], normmals[i]));
    }
    return ret;
}

void SketchController::setShowLines(bool v)
{
    showLines = v;
}

void SketchController::setShowPoints(bool v)
{
    showPoints = v;
}
